/* 
Copyright (c) 2008 Delio Brignoli

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/

#include <avr/io.h>
#include <stdlib.h>

/*

Adac supports one-shot async conversion and continuous async conversion.

// public API

- adac_init()
- adac_prepare()
- adac_trigger()
- adac_pause()
- adac_stop()

- adac_get_state() INVALID, STOPPED, READY, RUNNING

*/

#ifndef Adac_h
#define Adac_h

#ifdef __cplusplus
 extern "C" {
#endif 

#define ADAC_ERROR_OK 0
#define ADAC_ERROR_INVALID_STATE -10
#define ADAC_ERROR_INVALID_VALUE -11

typedef enum { 
ADAC_STATE_INVALID = 0, 
ADAC_STATE_STOPPED, 
ADAC_STATE_READY, 
ADAC_STATE_RUNNING,
ADAC_STATE_FINISHED,
} adac_state_t;

typedef enum { 
ADAC_TRIGGER_MODE_EXPLICIT = 0,
ADAC_TRIGGER_MODE_SOURCE = _BV(ADATE)
} adac_trigger_mode_t;

typedef enum { 
ADAC_TRIGGER_SOURCE_DONTCARE = 0,
ADAC_TRIGGER_SOURCE_FREERUNNING = 0,
ADAC_TRIGGER_SOURCE_ANALOG_COMPARATOR = _BV(ADTS0),
ADAC_TRIGGER_SOURCE_EXTERNAL_INTERRUPT_0 = _BV(ADTS1),
ADAC_TRIGGER_SOURCE_COUNTER_0_MATCH_A = _BV(ADTS0) | _BV(ADTS1),
ADAC_TRIGGER_SOURCE_COUNTER_0_OVERFLOW = _BV(ADTS2),
ADAC_TRIGGER_SOURCE_COUNTER_1_MATCH_B = _BV(ADTS0) | _BV(ADTS2),
ADAC_TRIGGER_SOURCE_COUNTER_1_OVERFLOW = _BV(ADTS1) | _BV(ADTS2),
ADAC_TRIGGER_SOURCE_COUNTER_1_CAPTURE_EVENT = _BV(ADTS0) | _BV(ADTS1) | _BV(ADTS2)
} adac_trigger_source_t;

typedef enum { 
ADAC_CLOCK_DIV_2 = _BV(ADPS0),
ADAC_CLOCK_DIV_4 = _BV(ADPS1),
ADAC_CLOCK_DIV_8 = _BV(ADPS0) | _BV(ADPS1),
ADAC_CLOCK_DIV_16 = _BV(ADPS2),
ADAC_CLOCK_DIV_32 = _BV(ADPS0) | _BV(ADPS2),
ADAC_CLOCK_DIV_64 = _BV(ADPS1) | _BV(ADPS2),
ADAC_CLOCK_DIV_128 = _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2),
} adac_clock_div_t;

typedef enum { 
ADAC_RESULT_ALIGNMENT_LEFT = _BV(ADLAR),
ADAC_RESULT_ALIGNMENT_RIGHT = 0x00
} adac_result_alignment_t;

typedef enum { 
ADAC_VOLTAGE_REFERENCE_AREF = 0x0,
ADAC_VOLTAGE_REFERENCE_AVCC = _BV(REFS0),
ADAC_VOLTAGE_REFERENCE_RESERVED = _BV(REFS1),
ADAC_VOLTAGE_REFERENCE_INTERNAL = _BV(REFS0) | _BV(REFS1)
} adac_voltage_ref_t;

typedef struct adac_internal_state {
  int conversion_num;
  int *inputs_array;
  int inputs_num;
  void *callback_priv;
  void (*conversion_complete_callback)(int, void*);

  int next_input;
  adac_result_alignment_t next_result_alignment;
  adac_trigger_mode_t next_trigger_mode;
  adac_trigger_source_t next_trigger_source;
  adac_clock_div_t next_clock_div;
  adac_voltage_ref_t next_voltage_reference;

} adac_internal_state_t;

#ifndef Adac_c
extern adac_internal_state_t _adac_priv;
#else
adac_internal_state_t _adac_priv;
#endif

// Public interface

adac_state_t adac_get_state(void);

// Initialize the internal state of the library. This function should be called
// before any other function in this library. For instance in the setup() function.
int adac_init( void );

// Prepare the inputs listed in inputs_array (at indexes from 0 to inputs_num-1) for
// analog to digital conversion. The callback function passed to adac_prepare() is called 
// once before returning to give the user a chance to set the conversion parameters.
// The priv pointer is an user provided pointer that will be passed as a parameter of the callback.
// Once the conversion is triggered, either explicitly or by external stimulus, the callback will be called
// after each conversion is complete. The callback can then setup another conversion or pause the conversion process.
int adac_prepare( int *inputs_array, size_t inputs_num, 
                                 void (*conversion_complete_callback)(int, void*),
							     void *priv );

// Expliclty start the conversion
int adac_trigger( void );

// Expliclty pause the conversion (the ADC will not be powered down and will be ready to start again).
int adac_pause( void );

// Conversion is stopped. To start another conversion, a call to prepare is required.
int adac_stop( void );

// Public interface to read back the result of the conversion

// FUNCTION TAKEN FROM: wiring_analog.c
static inline int adac_read_result( void ) {
  uint8_t high, low;

// we have to read ADCL first; doing so locks both ADCL
// and ADCH until ADCH is read.  reading ADCL second would
// cause the results of each conversion to be discarded,
// as ADCL and ADCH would be locked when it completed.

  low = ADCL;
  high = ADCH;
  // combine the two bytes
  return (high << 8) | low;
}

static inline int adac_read_result_hi8( void ) {
  uint8_t high;

  high = ADCH;
  return (high << 8);
}

// Public interface for manipulating the conversion process, 
// they should be called only from within the callback function

static inline void adac_set_next_input( int next_input ) {
  _adac_priv.next_input = next_input;
}

static inline void adac_set_next_clock_div( adac_clock_div_t next_clock_div ) {
  _adac_priv.next_clock_div = next_clock_div;
}

static inline void adac_set_next_voltage_reference( adac_voltage_ref_t next_voltage_reference ) {
  _adac_priv.next_voltage_reference = next_voltage_reference;
}

static inline void adac_set_next_result_alignment( adac_result_alignment_t next_result_alignment ) {
  _adac_priv.next_result_alignment = next_result_alignment;
}

static inline void adac_set_next_trigger_mode( adac_trigger_mode_t next_trigger_mode ) {
  _adac_priv.next_trigger_mode = next_trigger_mode;
}

static inline void adac_set_next_trigger_source( adac_trigger_source_t next_trigger_source ) {
  _adac_priv.next_trigger_source = next_trigger_source;
}

#ifdef __cplusplus
 }
#endif 

#endif
